/*
 * Code for blog.techhead.biz
 * Distributed under BSD-style license
 */

package biz.techhead.funcy;

import biz.techhead.funcy.Iterables.AbstractFuncyIterator;
import biz.techhead.funcy.Iterables.FuncyIterable;
import biz.techhead.funcy.Iterables.FuncyIterator;
import biz.techhead.funcy.Ranges.InclusiveIterableRange;
import biz.techhead.funcy.Ranges.OpenIterableRange;
import java.util.Collection;

/**
 * Provides specialized {@link Range} implementations for the object wrappers
 * of each of the integer-based primitive types
 * ({@code byte, char, short, int, long}).
 *
 * <p>When primitive types are required, it is far more efficient (speed wise)
 * to use the standard construct {@code for(int i=0; i<length; i++) {}} than
 * to iterate over a Range created from this class.  However, if what is
 * required is the object wrapper, then the performance of this class is
 * comparable to the do-it-yourself construct.
 *
 * @author Jonathan Hawkes <jhawkes at techhead.biz>
 */
class IntegerRanges {

    static OpenIterableRange<Character> from(Character from) {
        return new AscendingCharacterRange(from, Character.MAX_VALUE) {
            @Override
            public InclusiveIterableRange<Character> to(Character to) {
                int i = to;
                return (i < lower) ?
                    new DescendingCharacterRange(lower, i) :
                    super.to(to);
            }
        };
    }

    static InclusiveIterableRange<Character>
            from(Character from, Character to) {
        return ( to.compareTo(from) < 0 ) ?
            new DescendingCharacterRange(from, to) :
            new AscendingCharacterRange(from, to);
    }

    static OpenIterableRange<Character> upFrom(Character from) {
        return new AscendingCharacterRange(from, Character.MAX_VALUE);
    }

    static InclusiveIterableRange<Character>
            upFrom(Character from, Character to) {
        return new AscendingCharacterRange(from, to);
    }

    static OpenIterableRange<Character> downFrom(Character from) {
        return new DescendingCharacterRange(from, Character.MIN_VALUE);
    }    

    static InclusiveIterableRange<Character>
            downFrom(Character from, Character to) {
        return new DescendingCharacterRange(from, to);
    }    

    static OpenIterableRange<Byte> from(Byte from) {
        return new AscendingByteRange(from, Byte.MAX_VALUE) {
            @Override
            public InclusiveIterableRange<Byte> to(Byte to) {
                int i = to;
                return (i < lower) ?
                    new DescendingByteRange(lower, i) :
                    super.to(to);
            }
        };
    }

    static InclusiveIterableRange<Byte> from(Byte from, Byte to) {
        return ( to.compareTo(from) < 0 ) ?
            new DescendingByteRange(from, to) :
            new AscendingByteRange(from, to);
    }

    static OpenIterableRange<Byte> upFrom(Byte from) {
        return new AscendingByteRange(from, Byte.MAX_VALUE);
    }

    static InclusiveIterableRange<Byte>
            upFrom(Byte from, Byte to) {
        return new AscendingByteRange(from, to);
    }

    static OpenIterableRange<Byte> downFrom(Byte from) {
        return new DescendingByteRange(from, Byte.MIN_VALUE);
    }    

    static InclusiveIterableRange<Byte>
            downFrom(Byte from, Byte to) {
        return new DescendingByteRange(from, to);
    }    
    
    static OpenIterableRange<Short> from(Short from) {
        return new AscendingShortRange(from, Short.MAX_VALUE) {
            @Override
            public InclusiveIterableRange<Short> to(Short to) {
                int i = to;
                return (i < lower) ?
                    new DescendingShortRange(lower, i) :
                    super.to(to);
            }
        };
    }

    static InclusiveIterableRange<Short>
            from(Short from, Short to) {
        return ( to.compareTo(from) < 0 ) ?
            new DescendingShortRange(from, to) :
            new AscendingShortRange(from, to);
    }

    static OpenIterableRange<Short> upFrom(Short from) {
        return new AscendingShortRange(from, Short.MAX_VALUE);
    }
    
    static InclusiveIterableRange<Short>
            upFrom(Short from, Short to) {
        return new AscendingShortRange(from, to);
    }

    static OpenIterableRange<Short> downFrom(Short from) {
        return new DescendingShortRange(from, Short.MIN_VALUE);
    }    

    static InclusiveIterableRange<Short>
            downFrom(Short from, Short to) {
        return new DescendingShortRange(from, to);
    }

    static OpenIterableRange<Integer> from(Integer from) {
        return new AscendingIntegerRange(from, Integer.MAX_VALUE) {
            @Override
            public InclusiveIterableRange<Integer> to(Integer to) {
                int i = to;
                return (i < lower) ?
                    new DescendingIntegerRange(lower, i) :
                    super.to(to);
            }
        };
    }
    
    static InclusiveIterableRange<Integer>
            from(Integer from, Integer to) {
        return ( to.compareTo(from) < 0 ) ?
            new DescendingIntegerRange(from, to) :
            new AscendingIntegerRange(from, to);
    }

    static OpenIterableRange<Integer> upFrom(Integer from) {
        return new AscendingIntegerRange(from, Integer.MAX_VALUE);
    }

    static InclusiveIterableRange<Integer>
            upFrom(Integer from, Integer to) {
        return new AscendingIntegerRange(from, to);
    }

    static OpenIterableRange<Integer> downFrom(Integer from) {
        return new DescendingIntegerRange(from, Integer.MIN_VALUE);
    }    

    static InclusiveIterableRange<Integer>
            downFrom(Integer from, Integer to) {
        return new DescendingIntegerRange(from, to);
    }

    static OpenIterableRange<Long> from(Long from) {
        return new AscendingLongRange(from, Long.MAX_VALUE) {
            @Override
            public InclusiveIterableRange<Long> to(Long to) {
                long l = to;
                return (l < lower) ?
                    new DescendingLongRange(lower, l) :
                    super.to(to);
            }
        };
    }

    static InclusiveIterableRange<Long> from(Long from, Long to) {
        return ( to.compareTo(from) < 0 ) ?
            new DescendingLongRange(from, to) :
            new AscendingLongRange(from, to);
    }

    static OpenIterableRange<Long> upFrom(Long from) {
        return new AscendingLongRange(from, Long.MAX_VALUE);
    }

    static InclusiveIterableRange<Long>
            upFrom(Long from, Long to) {
        return new AscendingLongRange(from, to);
    }

    static OpenIterableRange<Long> downFrom(Long from) {
        return new DescendingLongRange(from, Long.MIN_VALUE);
    }    

    static InclusiveIterableRange<Long>
            downFrom(Long from, Long to) {
        return new DescendingLongRange(from, to);
    }


    private static abstract class AbstractIntegerRange<T>
            implements OpenIterableRange<T>, InclusiveIterableRange<T> {
        
        final int lower;
        final int upper;
        
        AbstractIntegerRange(int lower, int upper) {
            this.lower = lower;
            this.upper = upper;
        }

        abstract T box(int i);
        abstract int unbox(T t);

        @Override
        public boolean contains(T t) {
            if (t == null) {
                return false;
            } else {
                int i = unbox(t);
                return (i >= lower) && (i <= upper);
            }
        }
    }

    private static abstract class AbstractAscendingIntegerRange<T>
            extends AbstractIntegerRange<T> {
        
        private final int step;
        private final boolean upperTooCloseToMAX;

        AbstractAscendingIntegerRange(int lower, int upper, int step) {
            super(lower, upper);
            // absolute value of step
            this.step = step = (step < 0) ? -step : step;
            // if upper is too close to Integer.MAX_VALUE,
            // we'll have to use a different loop structure
            upperTooCloseToMAX = upper > Integer.MAX_VALUE - step;
        }

        @Override
        public FuncyIterable<T> by(Step<T> step) {
            throw new UnsupportedOperationException();
        }

        @Override
        public <O,E extends Throwable>
                FuncyIterable<O> map(FuncE<O,? super T,E> f) throws E {
            return Iterables.map(this, Funcs.asFunc(f));
        }

        @Override
        public <O,C extends Collection<? super O>,E extends Throwable>
                C map(FuncE<O,? super T,E> f, C out) throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    int i = lower;
                    out.add( f.call( box(i) ) );
                    for (int max=upper-step; i<=max;) {
                        out.add( f.call( box(i+=step) ) );
                    }
                }
            } else {
                for (int i=lower; i<=upper; i+=step) {
                    out.add( f.call( box(i) ) );
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                FuncyIterable<T> subset(FuncE<Boolean,? super T,E> f) throws E {
            return Iterables.subset(this, Funcs.asFunc(f));
        }

        @Override
        public <C extends Collection<? super T>,
                E extends Throwable>
                C subset(FuncE<Boolean,? super T,E> f, C out)
                throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    int i = lower;
                    T t = box(i);
                    if ( f.call(t) ) out.add(t);
                    for (int max=upper-step; i<=max;) {
                        t = box(i+=step);
                        if ( f.call(t) ) out.add(t);
                    }
                }
            } else {
                for (int i=lower; i<=upper; i+=step) {
                    T t = box(i);
                    if ( f.call(t) ) out.add(t);
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                void each(FuncE<?,? super T, E> f) throws E {
            if (upperTooCloseToMAX) {                
                if (lower <= upper) {
                    int i = lower;
                    f.call( box(i) );
                    for (int max=upper-step; i<=max;) {
                        f.call( box(i+=step) );
                    }
                }
            } else {
                for (int i=lower; i<=upper; i+=step) {
                    f.call( box(i) );
                }
            }
        }

        @Override
        public <E extends Throwable>
                boolean every(FuncE<Boolean,? super T, E> f)
                throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    int i = lower;
                    if ( !f.call( box(i) ) ) return false;
                    for (int max=upper-step; i<=max;) {
                        if ( !f.call( box(i+=step) ) ) return false;
                    }
                }
            } else {
                for (int i=lower; i<=upper; i+=step) {
                    if ( !f.call( box(i) ) ) return false;
                }
            }
            return true;
        }

        @Override
        public <E extends Throwable>
                boolean any(FuncE<Boolean,? super T, E> f)
                throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    int i = lower;
                    if ( f.call( box(i) ) ) return true;
                    for (int max=upper-step; i<=max;) {
                        if ( f.call( box(i+=step) ) ) return true;
                    }
                }
            } else {
                for (int i=lower; i<=upper; i+=step) {
                    if ( f.call( box(i) ) ) return true;
                }
            }
            return false;
        }
        
        @Override
        public FuncyIterator<T> iterator() {
            return upperTooCloseToMAX ?
                new TooCloseIterator() :
                new RegularIterator();
        }

        private class RegularIterator extends AbstractFuncyIterator<T> {

            private int i = lower;

            @Override
            public boolean hasNext() {
                return (i <= upper);
            }

            @Override
            public T next() {
                T next = box(i);
                i += step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }

        private class TooCloseIterator extends AbstractFuncyIterator<T> {

            private int i = lower;
            private final int max = upper - step;
            private boolean keepGoing = (lower <= upper); // true unless
                                                          // range is empty
            @Override
            public boolean hasNext() {
                return ( keepGoing && (i <= max) ) ||
                        (keepGoing^true==(keepGoing=false)); // true once if
                                                             // keepGoing was
                                                             // true
            }

            @Override
            public T next() {
                T next = box(i);
                i += step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }
    }

    private static abstract class AbstractDescendingIntegerRange<T>
            extends AbstractIntegerRange<T> {
        
        private final int step;
        private final boolean lowerTooCloseToMIN;

        AbstractDescendingIntegerRange(int upper, int lower, int step) {
            super(lower, upper);
            // absolute value of step
            this.step = step = (step < 0) ? -step : step;
            // if lower is too close to Integer.MIN_VALUE,
            // we'll have to use a different loop structure
            lowerTooCloseToMIN = lower < Integer.MIN_VALUE + step;
        }

        @Override
        public FuncyIterable<T> by(Step<T> step) {
            throw new UnsupportedOperationException();
        }

        @Override
        public <O,E extends Throwable>
                FuncyIterable<O> map(FuncE<O,? super T,E> f) throws E {
            return Iterables.map(this, Funcs.asFunc(f));
        }

        @Override
        public <O,C extends Collection<? super O>,E extends Throwable>
                C map(FuncE<O,? super T,E> f, C out) throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    int i = upper;
                    out.add( f.call( box(i) ) );
                    for (int min=lower+step; i>=min;) {
                        out.add( f.call( box(i-=step) ) );
                    }
                }
            } else {
                for (int i=upper; i>=lower; i-=step) {
                    out.add( f.call( box(i) ) );
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                FuncyIterable<T> subset(FuncE<Boolean,? super T,E> f) throws E {
            return Iterables.subset(this, Funcs.asFunc(f));
        }

        @Override
        public <C extends Collection<? super T>,
                E extends Throwable>
                C subset(FuncE<Boolean,? super T,E> f, C out)
                throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    int i = upper;
                    T t = box(i);
                    if ( f.call(t) ) out.add(t);
                    for (int min=lower+step; i>=min;) {
                        t = box(i-=step);
                        if ( f.call(t) ) out.add(t);
                    }
                }
            } else {
                for (int i=upper; i>=lower; i-=step) {
                    T t = box(i);
                    if ( f.call(t) ) out.add(t);
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                void each(FuncE<?,? super T, E> f) throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    int i = upper;
                    f.call( box(i) );
                    for (int min=lower+step; i>=min;) {
                        f.call( box(i-=step) );
                    }
                }
            } else {
                for (int i=upper; i>=lower; i-=step) {
                    f.call( box(i) );
                }
            }
        }

        @Override
        public <E extends Throwable>
                boolean every(FuncE<Boolean,? super T, E> f)
                throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    int i = upper;
                    if ( !f.call( box(i) ) ) return false;
                    for (int min=lower+step; i>=min;) {
                        if ( !f.call( box(i-=step) ) ) return false;
                    }
                }
            } else {
                for (int i=upper; i>=lower; i-=step) {
                    if ( !f.call( box(i) ) ) return false;
                }
            }
            return true;
        }

        @Override
        public <E extends Throwable>
                boolean any(FuncE<Boolean,? super T, E> f)
                throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    int i = upper;
                    if ( f.call( box(i) ) ) return true;
                    for (int min=lower+step; i>=min;) {
                        if ( f.call( box(i-=step) ) ) return true;
                    }
                }
            } else {
                for (int i=upper; i>=lower; i-=step) {
                    if ( f.call( box(i) ) ) return true;
                }
            }
            return false;
        }
        
        @Override
        public FuncyIterator<T> iterator() {
            return lowerTooCloseToMIN ?
                new TooCloseIterator() :
                new RegularIterator();
        }

        private class RegularIterator extends AbstractFuncyIterator<T> {

            private int i = upper;

            @Override
            public boolean hasNext() {
                return (i >= lower);
            }

            @Override
            public T next() {
                T next = box(i);
                i -= step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }

        private class TooCloseIterator extends AbstractFuncyIterator<T> {

            private int i = upper;
            private final int min = lower + step;
            private boolean keepGoing = (upper >= lower); // true unless
                                                          // range is empty
            @Override
            public boolean hasNext() {
                return ( keepGoing && (i >= min) ) ||
                        (keepGoing^true==(keepGoing=false)); // true once if
                                                             // keepGoing was
                                                             // true
            }

            @Override
            public T next() {
                T next = box(i);
                i -= step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }
    }
    
    private static class AscendingCharacterRange
            extends AbstractAscendingIntegerRange<Character> {

        AscendingCharacterRange(int lower, int upper) {
            this(lower, upper, 1);
        }

        AscendingCharacterRange(int lower, int upper, int step) {
            super(lower, upper, step);
        }

        @Override final Character box(int i) { return (char) i; }
        @Override final int unbox(Character c) { return c; }

        @Override
        public InclusiveIterableRange<Character> to(Character upper) {
            return new AscendingCharacterRange(lower, upper);
        }

        @Override
        public AscendingCharacterRange exclusive() {
            return new AscendingCharacterRange(lower, upper-1);
        }

        @Override
        public AscendingCharacterRange by(Number step) {
            return new AscendingCharacterRange(lower, upper, step.intValue());
        }
    }

    private static class DescendingCharacterRange
            extends AbstractDescendingIntegerRange<Character> {

        DescendingCharacterRange(int upper, int lower) {
            this(upper, lower, 1);
        }

        DescendingCharacterRange(int upper, int lower, int step) {
            super(upper, lower, step);
        }

        @Override final Character box(int i) { return (char) i; }
        @Override final int unbox(Character c) { return c; }

        @Override
        public DescendingCharacterRange to(Character lower) {
            return new DescendingCharacterRange(upper, lower);
        }

        @Override
        public DescendingCharacterRange exclusive() {
            return new DescendingCharacterRange(upper, lower+1);
        }

        @Override
        public DescendingCharacterRange by(Number step) {
            return new DescendingCharacterRange(upper, lower, step.intValue());
        }
    }

    private static class AscendingByteRange
            extends AbstractAscendingIntegerRange<Byte> {

        AscendingByteRange(int lower, int upper) {
            this(lower, upper, 1);
        }

        AscendingByteRange(int lower, int upper, int step) {
            super(lower, upper, step);
        }

        @Override final Byte box(int i) { return (byte) i; }
        @Override final int unbox(Byte b) { return b; }

        @Override
        public InclusiveIterableRange<Byte> to(Byte upper) {
            return new AscendingByteRange(lower, upper);
        }

        @Override
        public AscendingByteRange exclusive() {
            return new AscendingByteRange(lower, upper-1);
        }

        @Override
        public AscendingByteRange by(Number step) {
            return new AscendingByteRange(lower, upper, step.intValue());
        }
    }

    private static class DescendingByteRange
            extends AbstractDescendingIntegerRange<Byte> {

        DescendingByteRange(int upper, int lower) {
            this(upper, lower, 1);
        }

        DescendingByteRange(int upper, int lower, int step) {
            super(upper, lower, step);
        }

        @Override final Byte box(int i) { return (byte) i; }
        @Override final int unbox(Byte b) { return b; }

        @Override
        public DescendingByteRange to(Byte lower) {
            return new DescendingByteRange(upper, lower);
        }

        @Override
        public DescendingByteRange exclusive() {
            return new DescendingByteRange(upper, lower+1);
        }

        @Override
        public DescendingByteRange by(Number step) {
            return new DescendingByteRange(upper, lower, step.intValue());
        }
    }

    private static class AscendingShortRange
            extends AbstractAscendingIntegerRange<Short> {

        AscendingShortRange(int lower, int upper) {
            this(lower, upper, 1);
        }

        AscendingShortRange(int lower, int upper, int step) {
            super(lower, upper, step);
        }

        @Override final Short box(int i) { return (short) i; }
        @Override final int unbox(Short s) { return s; }

        @Override
        public InclusiveIterableRange<Short> to(Short upper) {
            return new AscendingShortRange(lower, upper);
        }

        @Override
        public AscendingShortRange exclusive() {
            return new AscendingShortRange(lower, upper-1);
        }

        @Override
        public AscendingShortRange by(Number step) {
            return new AscendingShortRange(lower, upper, step.intValue());
        }
    }

    private static class DescendingShortRange
            extends AbstractDescendingIntegerRange<Short> {

        DescendingShortRange(int upper, int lower) {
            this(upper, lower, 1);
        }

        DescendingShortRange(int upper, int lower, int step) {
            super(upper, lower, step);
        }

        @Override final Short box(int i) { return (short) i; }
        @Override final int unbox(Short s) { return s; }

        @Override
        public DescendingShortRange to(Short lower) {
            return new DescendingShortRange(upper, lower);
        }

        @Override
        public DescendingShortRange exclusive() {
            return new DescendingShortRange(upper, lower+1);
        }

        @Override
        public DescendingShortRange by(Number step) {
            return new DescendingShortRange(upper, lower, step.intValue());
        }
    }

    private static class AscendingIntegerRange
            extends AbstractAscendingIntegerRange<Integer> {

        AscendingIntegerRange(int lower, int upper) {
            this(lower, upper, 1);
        }

        AscendingIntegerRange(int lower, int upper, int step) {
            super(lower, upper, step);
        }

        @Override final Integer box(int i) { return i; }
        @Override final int unbox(Integer i) { return i; }

        @Override
        public InclusiveIterableRange<Integer> to(Integer upper) {
            return new AscendingIntegerRange(lower, upper);
        }

        @Override
        public AscendingIntegerRange exclusive() {
            return new AscendingIntegerRange(lower, upper-1);
        }

        @Override
        public AscendingIntegerRange by(Number step) {
            return new AscendingIntegerRange(lower, upper, step.intValue());
        }
    }

    private static class DescendingIntegerRange
            extends AbstractDescendingIntegerRange<Integer> {

        DescendingIntegerRange(int upper, int lower) {
            this(upper, lower, 1);
        }

        DescendingIntegerRange(int upper, int lower, int step) {
            super(upper, lower, step);
        }

        @Override final Integer box(int i) { return i; }
        @Override final int unbox(Integer i) { return i; }

        @Override
        public DescendingIntegerRange to(Integer lower) {
            return new DescendingIntegerRange(upper, lower);
        }

        @Override
        public DescendingIntegerRange exclusive() {
            return new DescendingIntegerRange(upper, lower+1);
        }

        @Override
        public DescendingIntegerRange by(Number step) {
            return new DescendingIntegerRange(upper, lower, step.intValue());
        }
    }

    private static abstract class AbstractLongRange
            implements OpenIterableRange<Long>, InclusiveIterableRange<Long> {
        
        final long lower;
        final long upper;
        
        AbstractLongRange(long lower, long upper) {
            this.lower = lower;
            this.upper = upper;
        }

        @Override
        public boolean contains(Long val) {
            if (val == null) {
                return false;
            } else {
                long l = val;
                return (l >= lower) && (l <= upper);
            }
        }
    }

    private static class AscendingLongRange
            extends AbstractLongRange {
        
        private final long step;
        private final boolean upperTooCloseToMAX;

        AscendingLongRange(long lower, long upper) {
            this(lower, upper, 1);
        }

        AscendingLongRange(long lower, long upper, long step) {
            super(lower, upper);
            // absolute value of step
            this.step = step = (step < 0) ? -step : step;
            // if upper is too close to Long.MAX_VALUE,
            // we'll have to use a different loop structure
            upperTooCloseToMAX = upper > Long.MAX_VALUE - step;
        }

        @Override
        public InclusiveIterableRange<Long> to(Long upper) {
            return new AscendingLongRange(lower, upper);
        }

        @Override
        public AscendingLongRange exclusive() {
            return new AscendingLongRange(lower, upper-1);
        }

        @Override
        public AscendingLongRange by(Number step) {
            return new AscendingLongRange(lower, upper, step.longValue());
        }

        @Override
        public Iterables.FuncyIterable<Long> by(Step<Long> step) {
            throw new UnsupportedOperationException();
        }

        @Override
        public <O,E extends Throwable>
                FuncyIterable<O> map(FuncE<O,? super Long,E> f) throws E {
            return Iterables.map(this, Funcs.asFunc(f));
        }

        @Override
        public <O,C extends Collection<? super O>,E extends Throwable>
                C map(FuncE<O,? super Long,E> f, C out) throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    long i = lower;
                    out.add( f.call(i) );
                    for (long max=upper-step; i<=max;) {
                        out.add( f.call(i+=step) );
                    }
                }
            } else {
                for (long i=lower; i<=upper; i+=step) {
                    out.add( f.call(i) );
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                FuncyIterable<Long> subset(FuncE<Boolean,? super Long,E> f)
                throws E {
            return Iterables.subset(this, Funcs.asFunc(f));
        }

        @Override
        public <C extends Collection<? super Long>,
                E extends Throwable>
                C subset(FuncE<Boolean,? super Long,E> f, C out)
                throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    long i = lower;
                    Long t = i;
                    if ( f.call(t) ) out.add(t);
                    for (long max=upper-step; i<=max;) {
                        t = (i+=step);
                        if ( f.call(t) ) out.add(t);
                    }
                }
            } else {
                for (long i=lower; i<=upper; i+=step) {
                    Long t = i;
                    if ( f.call(t) ) out.add(t);
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                void each(FuncE<?,? super Long, E> f) throws E {
            if (upperTooCloseToMAX) {                
                if (lower <= upper) {
                    long i = lower;
                    f.call(i);
                    for (long max=upper-step; i<=max;) {
                        f.call(i+=step);
                    }
                }
            } else {
                for (long i=lower; i<=upper; i+=step) {
                    f.call(i);
                }
            }
        }

        @Override
        public <E extends Throwable>
                boolean every(FuncE<Boolean,? super Long, E> f)
                throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    long i = lower;
                    if ( !f.call(i) ) return false;
                    for (long max=upper-step; i<=max;) {
                        if ( !f.call(i+=step) ) return false;
                    }
                }
            } else {
                for (long i=lower; i<=upper; i+=step) {
                    if ( !f.call(i) ) return false;
                }
            }
            return true;
        }

        @Override
        public <E extends Throwable>
                boolean any(FuncE<Boolean,? super Long, E> f)
                throws E {
            if (upperTooCloseToMAX) {
                if (lower <= upper) {
                    long i = lower;
                    if ( f.call(i) ) return true;
                    for (long max=upper-step; i<=max;) {
                        if ( f.call(i+=step) ) return true;
                    }
                }
            } else {
                for (long i=lower; i<=upper; i+=step) {
                    if ( f.call(i) ) return true;
                }
            }
            return false;
        }
        
        @Override
        public FuncyIterator<Long> iterator() {
            return upperTooCloseToMAX ?
                new TooCloseIterator() :
                new RegularIterator();
        }

        private class RegularIterator extends AbstractFuncyIterator<Long> {

            private long i = lower;

            @Override
            public boolean hasNext() {
                return (i <= upper);
            }

            @Override
            public Long next() {
                Long next = i;
                i += step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }

        private class TooCloseIterator extends AbstractFuncyIterator<Long> {

            private long i = lower;
            private final long max = upper - step;
            private boolean keepGoing = (lower <= upper); // true unless
                                                          // range is empty
            @Override
            public boolean hasNext() {
                return ( keepGoing && (i <= max) ) ||
                        (keepGoing^true==(keepGoing=false)); // true once if
                                                             // keepGoing was
                                                             // true
            }

            @Override
            public Long next() {
                Long next = i;
                i += step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }
    }
    
    private static class DescendingLongRange
            extends AbstractLongRange {
        
        private final long step;
        private final boolean lowerTooCloseToMIN;

        DescendingLongRange(long upper, long lower) {
            this(upper, lower, 1);
        }

        DescendingLongRange(long upper, long lower, long step) {
            super(lower, upper);
            // absolute value of step
            this.step = step = (step < 0) ? -step : step;
            // if lower is too close to Integer.MIN_VALUE,
            // we'll have to use a different loop structure
            lowerTooCloseToMIN = lower < Integer.MIN_VALUE + step;
        }

        @Override
        public DescendingLongRange to(Long lower) {
            return new DescendingLongRange(upper, lower);
        }

        @Override
        public DescendingLongRange exclusive() {
            return new DescendingLongRange(upper, lower+1);
        }

        @Override
        public DescendingLongRange by(Number step) {
            return new DescendingLongRange(upper, lower, step.longValue());
        }

        @Override
        public Iterables.FuncyIterable<Long> by(Step<Long> step) {
            throw new UnsupportedOperationException();
        }

        @Override
        public <O,E extends Throwable>
                FuncyIterable<O> map(FuncE<O,? super Long,E> f) throws E {
            return Iterables.map(this, Funcs.asFunc(f));
        }

        @Override
        public <O,C extends Collection<? super O>,E extends Throwable>
                C map(FuncE<O,? super Long,E> f, C out) throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    long i = upper;
                    out.add( f.call(i) );
                    for (long min=lower+step; i>=min;) {
                        out.add( f.call(i-=step) );
                    }
                }
            } else {
                for (long i=upper; i>=lower; i-=step) {
                    out.add( f.call(i) );
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                FuncyIterable<Long> subset(FuncE<Boolean,? super Long,E> f)
                throws E {
            return Iterables.subset(this, Funcs.asFunc(f));
        }

        @Override
        public <C extends Collection<? super Long>,
                E extends Throwable>
                C subset(FuncE<Boolean,? super Long,E> f, C out)
                throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    long i = upper;
                    Long t = i;
                    if ( f.call(t) ) out.add(t);
                    for (long min=lower+step; i>=min;) {
                        t = (i-=step);
                        if ( f.call(t) ) out.add(t);
                    }
                }
            } else {
                for (long i=upper; i>=lower; i-=step) {
                    Long t = i;
                    if ( f.call(t) ) out.add(t);
                }
            }
            return out;
        }

        @Override
        public <E extends Throwable>
                void each(FuncE<?,? super Long, E> f) throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    long i = upper;
                    f.call(i);
                    for (long min=lower+step; i>=min;) {
                        f.call(i-=step);
                    }
                }
            } else {
                for (long i=upper; i>=lower; i-=step) {
                    f.call(i);
                }
            }
        }

        @Override
        public <E extends Throwable>
                boolean every(FuncE<Boolean,? super Long, E> f)
                throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    long i = upper;
                    if ( !f.call(i) ) return false;
                    for (long min=lower+step; i>=min;) {
                        if ( !f.call(i-=step) ) return false;
                    }
                }
            } else {
                for (long i=upper; i>=lower; i-=step) {
                    if ( !f.call(i) ) return false;
                }
            }
            return true;
        }

        @Override
        public <E extends Throwable>
                boolean any(FuncE<Boolean,? super Long, E> f)
                throws E {
            if (lowerTooCloseToMIN) {
                if (upper >= lower) {
                    long i = upper;
                    if ( f.call(i) ) return true;
                    for (long min=lower+step; i>=min;) {
                        if ( f.call(i-=step) ) return true;
                    }
                }
            } else {
                for (long i=upper; i>=lower; i-=step) {
                    if ( f.call(i) ) return true;
                }
            }
            return false;
        }
        
        @Override
        public FuncyIterator<Long> iterator() {
            return lowerTooCloseToMIN ?
                new TooCloseIterator() :
                new RegularIterator();
        }

        private class RegularIterator extends AbstractFuncyIterator<Long> {

            private long i = upper;

            @Override
            public boolean hasNext() {
                return (i >= lower);
            }

            @Override
            public Long next() {
                Long next = i;
                i -= step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }

        private class TooCloseIterator extends AbstractFuncyIterator<Long> {

            private long i = upper;
            private final long min = lower + step;
            private boolean keepGoing = (upper >= lower); // true unless
                                                          // range is empty
            @Override
            public boolean hasNext() {
                return ( keepGoing && (i >= min) ) ||
                        (keepGoing^true==(keepGoing=false)); // true once if
                                                             // keepGoing was
                                                             // true
            }

            @Override
            public Long next() {
                Long next = i;
                i -= step;
                return next;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }
    }

}
